﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Mail;
using System.ServiceProcess;
using System.Threading;
using System.Xml.Serialization;
using CloudMailShared;
using Microsoft.WindowsAzure.StorageClient;

namespace CloudEmailRelay
{
    public partial class Service1 : ServiceBase
    {
        private const int POLL_INTERVAL_ON_ERROR = 1000 * 60 * 15; //Fifteen Mins

        private Properties.Settings applicationSettings;
        private MailCloudStorage mailCloudStorage = null;
        private SmtpClient smtpClient;

        /// <summary>
        /// Initializes a new instance of the <see cref="Service1"/> class.
        /// </summary>
        public Service1()
        {
            try
            {
                InitializeComponent();
                this.AutoLog = true;
                applicationSettings = new Properties.Settings();
            }
            catch (Exception ex)
            {
                this.EventLog.WriteEntry(ex.Message+"::"+ex.StackTrace, EventLogEntryType.Error);
                this.Stop();
            }
        }

        /// <summary>
        /// Executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating system starts (for a service that starts automatically). Specifies actions to take when the service starts.
        /// </summary>
        /// <param name="args">Data passed by the start command.</param>
        protected override void OnStart(string[] args)
        {
            try
            {
                smtpClient = new SmtpClient(applicationSettings.SmtpServer, applicationSettings.SmtpPort)
                {
                    EnableSsl = applicationSettings.SmtpEnableSsl
                };

                //Only assign credentials if we have a username else use default credentials
                if (applicationSettings.SmtpUsername != null && applicationSettings.SmtpUsername.Length > 0)
                {
                    smtpClient.UseDefaultCredentials = false;

                    smtpClient.Credentials = new NetworkCredential(
                        applicationSettings.SmtpUsername,
                        applicationSettings.SmptPassword);
                }
                else
                {
                    smtpClient.UseDefaultCredentials = true;
                }

                // initialize the storage account
                if (applicationSettings.UseDevFabric)
                {
                    mailCloudStorage = new MailCloudStorage(applicationSettings.MailQueueName);
                }
                else
                {
                    mailCloudStorage = new MailCloudStorage(applicationSettings.AzureStorageUrl, applicationSettings.MailQueueName);
                }
            }
            catch (Exception ex)
            {
                this.EventLog.WriteEntry(ex.Message+"::"+ex.StackTrace, EventLogEntryType.Error);
                this.Stop();
            }

            bkgWorker1.RunWorkerAsync();
        }

        /// <summary>
        /// Writes the debug message.
        /// </summary>
        /// <param name="debugMessage">The debug message.</param>
        private void writeDebugMessage(string debugMessage)
        {
            if (applicationSettings.GenerateDebugMessages)
            {
                this.EventLog.WriteEntry(debugMessage, EventLogEntryType.Information);
            }
        }

        /// <summary>
        /// Executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
        /// </summary>
        protected override void OnStop()
        {
            bkgWorker1.CancelAsync();
        }

        /// <summary>
        /// Handles the DoWork event of the bkgWorker1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void bkgWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            XmlSerializer mailMessageSerializer = new XmlSerializer(typeof(SerializableMailMessage));
            TimeSpan visibilityTimeout = new TimeSpan(0, 2, 0);
            
            CloudQueueMessage msg = null;
            SerializableMailMessage mailMessage = null;
            CloudBlob blob = null;

            this.EventLog.WriteEntry(
                "Worker started..checking for email every " + 
                applicationSettings.PollIntervalSeconds.ToString() + " seconds", 
                EventLogEntryType.Information);

            // retrieve messages and send mail...forever
            while (true)
            {
                writeDebugMessage("Checking for mail");

                //loop while there are still messages on the queue
                while ((msg = mailCloudStorage.MailQueue.GetMessage(visibilityTimeout)) != null)
                {
                    try
                    {
                        writeDebugMessage("Found message "+msg.AsString);

                        //Get the message content and send it
                        blob = mailCloudStorage.MailStorageContainer.GetBlobReference(msg.AsString);

                        using (BlobStream blobstream = blob.OpenRead())
                        {
                            mailMessage = (SerializableMailMessage)mailMessageSerializer.Deserialize(blobstream);
                            blobstream.Close();
                        }

                        writeDebugMessage("Sending message " + msg.AsString);
                        smtpClient.Send(mailMessage);

                        //The message has been sent so delete it
                        writeDebugMessage("Deleting message " + msg.AsString);
                        mailCloudStorage.MailQueue.DeleteMessage(msg);
                        blob.Delete();
                    }
                    catch (Exception ex)
                    {
                        this.EventLog.WriteEntry(ex.Message + "::"+ex.StackTrace, EventLogEntryType.Error);

                        try
                        {
                            //Something has gone wrong so put the message on the back of the queue and leave the data alone
                            mailCloudStorage.MailQueue.AddMessage(new CloudQueueMessage(msg.AsString));
                            mailCloudStorage.MailQueue.DeleteMessage(msg);
                        }
                        catch (Exception innerEx)
                        {
                            this.EventLog.WriteEntry(innerEx.Message+"::"+innerEx.StackTrace, EventLogEntryType.Error);
                        }

                        //There probably is connection problems so take a long pause
                        Thread.Sleep(POLL_INTERVAL_ON_ERROR);
                    }
                }

                //Sleep for the configured time
                writeDebugMessage("Pausing");
                Thread.Sleep(applicationSettings.PollIntervalSeconds*1000);
            }
        }
    }
}